﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.ComponentModel;
using System.Reflection;
using System.Globalization;
using System.Resources;
using EnvDTE;
using System.Data;

namespace SPVisualDev
{
    internal class PropSheetBase : IPropertySheet
    {
        private XmlElement m_xml_element;

        [Browsable(true), DisplayName("Element file name"), RefreshProperties(RefreshProperties.None), TypeConverter(typeof(ElementFileTypeExpandableConverter))]
        public virtual string ElementFileName
        {
            get;
            set;
        }

        [Browsable(false)]
        public ElementInfo ElementInfo
        {
            get;
            set;
        }

        [Browsable(false)]
        public string DefaultResourceFile
        {
            get;
            set;
        }

        [Browsable(false)]
        public ProjectItem ParentFeatureFolder
        {
            get;
            set;
        }

        [Browsable(false)]
        public bool HasUnsavedResourceChanges
        {
            get
            {
                foreach (PropertyInfo pInfo in GetAllManagedProperties())
                {
                    if (pInfo.PropertyType == typeof(ResourceStringContainer))
                    {

                        ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                        if (container.HasUnsavedChanges && !container.IsResourceReadOnly)
                            return true;
                    }
                }


                return false;

            }
            set
            {
                foreach (PropertyInfo pInfo in GetAllManagedProperties())
                {
                    if (pInfo.PropertyType == typeof(ResourceStringContainer))
                    {
                        ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                        container.HasUnsavedChanges = value;
                    }
                }
            }
        }

        [Browsable(false)]
        public virtual XmlElement PropXmlNode
        {
            get
            {
                if (m_xml_element == null)
                    return null;

                List<PropertyInfo> list = GetAllManagedProperties();
                foreach (PropertyInfo pInfo in list)
                {
                    bool excluded = false;
                    if (m_xml_element.GetAttributeNode(pInfo.Name) == null && !this.ElementInfo.New)
                    {
                        excluded = true;
                    }

                    if (pInfo.PropertyType == typeof(ResourceStringContainer))
                    {
                        ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                        string value;
                        if (container.RawString == null)
                            value = string.Empty;
                        else
                            value = container.RawString;

                        if (!excluded || !string.IsNullOrEmpty(value))
                        {
                            Common.SetXmlAttribute(m_xml_element, pInfo.Name, value);
                        }
                    }
                    else
                    {
                        object o = pInfo.GetValue(this, null);
                        if (o != null)
                        {
                            string raw_str = pInfo.GetValue(this, null).ToString();

                            if (!excluded || !string.IsNullOrEmpty(raw_str))
                            {
                                if (pInfo.PropertyType == typeof(bool) || pInfo.PropertyType.Name == "Nullable`1")
                                    Common.SetXmlAttributeIgnoreIfValueMatches(m_xml_element, pInfo.Name, raw_str);
                                else
                                    Common.SetXmlAttribute(m_xml_element, pInfo.Name, raw_str);
                            }
                        }
                    }
                }

                return m_xml_element;
            }
            set
            {
                m_xml_element = value;
                if (m_xml_element == null)
                    return;
                
                List<PropertyInfo> list = GetAllManagedProperties();
                foreach (PropertyInfo pInfo in list)
                {
                    string raw_str = Common.GetXmlAttribute(m_xml_element, pInfo.Name);

                    if (pInfo.PropertyType.Name == "Nullable`1")
                    {
                        if (!string.IsNullOrEmpty(raw_str))
                            pInfo.SetValue(this, bool.Parse(raw_str), null);
                    }
                    else if (pInfo.PropertyType == typeof(ResourceStringContainer))
                    {
                        ResourceStringContainer container = new ResourceStringContainer(raw_str, this.DefaultResourceFile, this.ParentFeatureFolder);
                        if (pInfo.PropertyType == typeof(ResourceStringContainer))
                        {
                            pInfo.SetValue(this, container, null);
                        }
                    }
                    else
                    {
                        if (pInfo.PropertyType == typeof(string))
                            pInfo.SetValue(this, raw_str, null);
                        else if (pInfo.PropertyType == typeof(int) && !string.IsNullOrEmpty(raw_str))
                            pInfo.SetValue(this, int.Parse(raw_str), null);
                        else if (pInfo.PropertyType == typeof(bool) && !string.IsNullOrEmpty(raw_str))
                            pInfo.SetValue(this, bool.Parse(raw_str), null);

                    }
                }

                this.HasUnsavedResourceChanges = false;
            }
        }

        private List<PropertyInfo> GetAllManagedProperties()
        {
            List<PropertyInfo> list = new List<PropertyInfo>();

            PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo pInfo in props)
            {
                object[] o = pInfo.GetCustomAttributes(typeof(CategoryAttribute), false);
                if (o.Length > 0)
                {
                    string cat = (o[0] as CategoryAttribute).Category;
                    if (cat == "Properties" ||
                        cat == "Main info")
                    {
                        list.Add(pInfo);
                    }
                }

            }

            return list;
        }

        public override string ToString()
        {
            return base.ToString();
        }

        public void InvalidateResourceChache()
        {
            foreach (PropertyInfo pInfo in GetAllManagedProperties())
            {
                if (pInfo.PropertyType == typeof(ResourceStringContainer))
                {
                    ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                    container.InvalidateCahe();
                }
            }

        }

        public void SetNewDefaultResourceFile(string ResourceFile)
        {
            this.DefaultResourceFile = ResourceFile;

            foreach (PropertyInfo pInfo in GetAllManagedProperties())
            {
                if (pInfo.PropertyType == typeof(ResourceStringContainer))
                {
                    ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                    container.DefaultResourceFile = ResourceFile;
                }
            }
        }

        public void SaveChanges()
        {
            List<PropertyInfo> list = GetAllManagedProperties();
            foreach (PropertyInfo pInfo in list)
            {
                if (pInfo.PropertyType != typeof(ResourceStringContainer))
                    continue;

                ResourceStringContainer res_container = pInfo.GetValue(this, null) as ResourceStringContainer;
                string raw_str = res_container.RawString;

                if (!ResourceHelper.IsResourceString(raw_str))
                    continue;

                ResourceKeyInfo key_info = ResourceHelper.ResolveRawStringDefinition(raw_str, DefaultResourceFile, ParentFeatureFolder);

                if (key_info.ProjectItem != null)
                {
                    string dir = Path.GetDirectoryName(key_info.Path);
                    string file = Path.GetFileNameWithoutExtension(key_info.Path);

                    IDictionary<string, string> dict = res_container.GetAllCultureValues(true);
                    foreach (KeyValuePair<string, string> kvp in dict)
                    {
                        string path = dir + "\\" + file;
                        if (kvp.Key != string.Empty)
                            path += "." + kvp.Key + ".resx";
                        else
                            path += ".resx";

                        ProjectItem proj_item = null;
                        try
                        {
                            proj_item = key_info.ProjectItem.Collection.Item(Path.GetFileName(path));
                        }
                        catch { }

                        if (proj_item != null)
                        {
                            if (!Common.CheckOutItemFromSCC(proj_item))
                                continue;

                            XmlDataDocument xmldoc = new XmlDataDocument();
                            xmldoc.PreserveWhitespace = true;
                            xmldoc.DataSet.ReadXml(path);

                            List<ResXDataNode> resx_list = new List<ResXDataNode>();
                            DataTable table = xmldoc.DataSet.Tables["data"];
                            foreach (DataRow row in table.Rows)
                            {
                                ResXDataNode node = new ResXDataNode(row["name"] as string, row["value"] as string);
                                if (table.Columns.Contains("comments"))
                                    node.Comment = row["comments"] as string;

                                resx_list.Add(node);
                            }

                            bool updated = false;
                            using (ResXResourceWriter writer = new ResXResourceWriter(path))
                            {
                                foreach (ResXDataNode node in resx_list)
                                {
                                    if (node.Name == key_info.ResourceKey)
                                    {
                                        writer.AddResource(key_info.ResourceKey, kvp.Value);
                                        updated = true;
                                    }
                                    else
                                    {
                                        writer.AddResource(node);
                                    }
                                }

                                if (!updated)
                                {
                                    writer.AddResource(key_info.ResourceKey, kvp.Value);
                                }

                                writer.Generate();
                                writer.Close();
                            }
                        }
                    }
                }
            }

            this.HasUnsavedResourceChanges = false;
        }

        public ResourceStringContainer CreateResourceStringContainer(string RawString)
        {
            return new ResourceStringContainer(RawString, this.DefaultResourceFile, this.ParentFeatureFolder);
        }

        public void SetRuntimeAttributeArgument(string Property, Type attribute, string Field, object Value)
        {
            PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())[Property];
            Attribute a = descriptor.Attributes[attribute];
            FieldInfo field = a.GetType().GetField(Field, BindingFlags.NonPublic | BindingFlags.Instance);
            field.SetValue(a, Value);
        }

        [Browsable(false)]
        public bool ElementFileNameHasChanged
        {
            get;
            set;
        }

        public class ElementFileTypeExpandableConverter : ExpandableObjectConverter
        {
            public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
            {
                return false;
            }

            public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
            {
                return true;
            }

            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                return true;
            }

            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                return true;
            }

            public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
            {
                PropSheetBase obj = context.Instance as PropSheetBase;

                List<string> valid_element_files = new List<string>();

                foreach (ProjectItem item in obj.ParentFeatureFolder.ProjectItems)
                {
                    if (item.Kind != Constants.vsProjectItemKindPhysicalFile)
                        continue;

                    if (Path.GetExtension(item.Name.ToLower()) != ".xml")
                        continue;

                    XmlDocument xmldoc = new XmlDocument();
                    xmldoc.Load(Common.GetProjectItemPath(item));

                    if (xmldoc.DocumentElement.LocalName == "Elements")
                    {
                        valid_element_files.Add(item.Name);
                    }
                }

                return new StandardValuesCollection(valid_element_files);
            }

            public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
            {
                PropSheetBase prop_sheet = context.Instance as PropSheetBase;

                string s = value as string;
                if (!string.IsNullOrEmpty(s))
                {
                    string name = s;
                    if (name.LastIndexOf(".") != -1)
                        name = name.Substring(0, name.LastIndexOf("."));

                    name = Common.FilterIllegalCharsInFileName(name) + ".xml";

                    prop_sheet.ElementFileNameHasChanged = true;

                    return name;

                }
                else
                {
                    return prop_sheet.ElementInfo.ElementFileName;
                }
            }

            public override bool GetPropertiesSupported(ITypeDescriptorContext context)
            {
                return false;
            }
        }
    }

    internal interface IPropertySheet
    {
        ElementInfo ElementInfo { get; set; }
        string ElementFileName { get; set; }
        string DefaultResourceFile { get; set; }
        ProjectItem ParentFeatureFolder { get; set; }
        XmlElement PropXmlNode { get; set; }
        bool HasUnsavedResourceChanges { get; set; }
        bool ElementFileNameHasChanged { get; set; }
        void InvalidateResourceChache();
        void SetNewDefaultResourceFile(string ResourceFile);
        void SaveChanges();
    }


    public class ResourceExpandableObjectConverter : ExpandableObjectConverter
    {
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            ResourceStringContainer container = value as ResourceStringContainer;
            IPropertySheet prop_sheet = context.Instance as IPropertySheet;

            if (!ResourceHelper.IsResourceString(container.RawString))
            {
                return null;
            }

            List<ResourceHelper.CultureFileInfo> list = ResourceHelper.GetAllAvailableCultures(container.RawString, prop_sheet.DefaultResourceFile, prop_sheet.ParentFeatureFolder);

            if (list.Count == 0)
            {
                ResourcePropertyDescriptor res_prop_descr = new ResourcePropertyDescriptor("No default resource file could be found.", attributes);
                res_prop_descr.SetReadOnlyFlag(true);

                return new PropertyDescriptorCollection(new ResourcePropertyDescriptor[] { res_prop_descr });
            }


            list.Sort(new ResourceCultureInfoComparer());

            ArrayList arr = new ArrayList();
            foreach (ResourceHelper.CultureFileInfo cFileInfo in list)
            {
                ResourcePropertyDescriptor res_prop_descr;
                if (cFileInfo.Culture.Name == string.Empty)
                {
                    res_prop_descr = new ResourcePropertyDescriptor("(Default)", attributes);
                }
                else
                {
                    res_prop_descr = new ResourcePropertyDescriptor(cFileInfo.Culture.Name + " " + cFileInfo.Culture.DisplayName, attributes);
                }

                if (cFileInfo.ProjectItem == null)
                {
                    res_prop_descr.SetReadOnlyFlag(true);
                }

                arr.Add(res_prop_descr);

            }

            PropertyDescriptorCollection coll = new PropertyDescriptorCollection(null);
            foreach (PropertyDescriptor d in arr)
                coll.Add(d);

            return coll;
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return (destinationType == typeof(string));
        }

        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(System.String))
                return true;
            else
                return base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            object ret;
            if (value is string)
            {
                ResourceStringContainer container = context.PropertyDescriptor.GetValue(context.Instance) as ResourceStringContainer;
                string raw_string = container.RawString;

                ret = new ResourceStringContainer(value as string, container.DefaultResourceFile, container.ParentFeatureFolder);

            }
            else
            {
                ret = base.ConvertFrom(context, culture, value);
            }

            return ret;
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (value is ResourceStringContainer)
            {
                ResourceStringContainer container = value as ResourceStringContainer;
                if (!ResourceHelper.IsResourceString(container.RawString))
                {
                    context.GetType().GetProperty("Expanded").SetValue(context, true, null);
                }
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }

        private class ResourceCultureInfoComparer : IComparer<ResourceHelper.CultureFileInfo>
        {
            #region IComparer<CultureInfo> Members

            public int Compare(ResourceHelper.CultureFileInfo x, ResourceHelper.CultureFileInfo y)
            {
                return x.Culture.Name.CompareTo(y.Culture.Name);
            }

            #endregion
        }
    }

    public class ResourcePropertyDescriptor : PropertyDescriptor
    {
        private bool m_is_readonly;

        public ResourcePropertyDescriptor(string Name, Attribute[] attributes)
            : base(Name, attributes)
        {
        }

        public void SetReadOnlyFlag(bool IsReadOnly)
        {
            m_is_readonly = IsReadOnly;
        }

        public override bool CanResetValue(object component)
        {
            return false;
        }

        public override Type ComponentType
        {
            get { throw new NotImplementedException(); }
        }

        public override bool IsReadOnly
        {
            get { return m_is_readonly; }
        }

        public override Type PropertyType
        {
            get { return typeof(string); }
        }

        public override void ResetValue(object component)
        {

        }

        public override object GetValue(object component)
        {
            ResourceStringContainer container = component as ResourceStringContainer;

            string s = this.Name;
            if (s == "(Default)")
                s = null;
            else
                s = this.Name.Split(' ')[0];

            return container.GetValue(s);
        }

        public override void SetValue(object component, object value)
        {
            ResourceStringContainer container = component as ResourceStringContainer;

            string s = this.Name;
            if (s == "(Default)")
                s = null;
            else
                s = this.Name.Split(' ')[0];

            container.SetValue(s, value as string);
        }

        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }
    }


}
